/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *	  http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.milib.tools {
	import com.googlecode.milib.base.IMLBroadcasterListener;
	import com.googlecode.milib.base.MLBroadcastClass;
	import com.googlecode.milib.data.value.MLBooleanValue;
	import com.googlecode.milib.data.value.MLNumberWithRangeValue;
	import com.googlecode.milib.service.ISharedObjectsServicedObject;
	import com.googlecode.milib.service.SharedObjectsObjectService;
	import com.googlecode.milib.service.singleton.EnterFrameService;
	import com.googlecode.milib.vo.MLEvent;
	import com.googlecode.milib.vo.MLEventInfo;

	import flash.events.NetStatusEvent;
	import flash.media.SoundTransform;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.URLRequest;

	/**
	 * @author Marek Brun
	 */
	public class FLVPlayer extends MLBroadcastClass implements IMLBroadcasterListener, ISharedObjectsServicedObject {

		public var event_NewFLVFile : String='NewFLVFile';
		public var event_MetaData : String='MetaData';
		public var event_MovieIsFinished : String='MovieIsFinished';
		public var event_Movie100MSBeforeFinished : String='Movie100MSBeforeFinished';
		
		//DATA:	BitmapData
		public var event_ScreenGrabbed:String='ScreenGrabbed';
		
		private var video:Video;
		private var connection:NetConnection;
		private var netStream:NetStream;
		private var metaData:Object; // {duration:109.45, creationdate:"Fri Sep 10 19:12:26 2004", framerate:24, audiodatarate:112, videodatarate:896, height:480, width:480}
		private var ef:EnterFrameService;
		
		public var isPlay:MLBooleanValue;
		public var isPlaying:MLBooleanValue;
		public var isBuffering:MLBooleanValue;
		public var isStreamNotFound:MLBooleanValue;
		public var volume:MLNumberWithRangeValue;
		public var loadingProgress:MLNumberWithRangeValue;
		public var playingProgress:MLNumberWithRangeValue;
		private var isRevindFlvAndPlay:Boolean;
		public var url:URLRequest;
		private var screenGrabber:FLVScreenGrabber;		public var isStartedStream:Boolean=false;		private var isNetStreamPlayStart:Boolean;
				public function FLVPlayer(video:Video) {
			this.video=video;
			connection=new NetConnection();
			connection.connect(null);
			
			
			isPlay=new MLBooleanValue(false);
			isPlay.addListener(this);
			isPlaying=new MLBooleanValue(false);			isBuffering=new MLBooleanValue(false);
			isStreamNotFound=new MLBooleanValue(false);
			volume=new MLNumberWithRangeValue(0, 0, 1);
			volume.addListener(this);
			loadingProgress=new MLNumberWithRangeValue(0, 0, 1);
			playingProgress=new MLNumberWithRangeValue(0, 0, 1);
			
			ef=EnterFrameService.getInstance();
			
			SharedObjectsObjectService.forInstance(this);
			
		}
		
		public function setupFileURL(url:URLRequest):void {
			this.url=url;
		}
		
		public function play():void {
			isStartedStream=true;
			metaData=null;
			isBuffering.v=true;
			netStream=new NetStream(connection);
			netStream.bufferTime=5;
			netStream.addEventListener(NetStatusEvent.NET_STATUS, onStreamNetStatus, false, 0, true);			netStream.client=this;
			video.attachNetStream(netStream);
			netStream.play(url.url);
			isPlay.v=true;
			ef.addEventListener(ef.event_EnterFrame, onEnterFrame);
			
			var videoVolumeTransform:SoundTransform = new SoundTransform();
			videoVolumeTransform.volume = volume.v;
			netStream.soundTransform = videoVolumeTransform;
		}
		
		public function setPlayingProgress(n01:Number):void {
			if(metaData.duration){
				netStream.seek(n01*metaData.duration);
			}
		}
		
		public function revindFlv(startFromSecond:Number=0):void{
			netStream.seek(startFromSecond);
		}
		
		public function revindFlvAndStop(startFromSecond:Number=0):void{
			isNetStreamPlayStart=false;
			revindFlv(startFromSecond);
			isPlay.v=false;
		}
		
		public function revindFlvAndPlay(startFromSecond:Number=0):void{
			isRevindFlvAndPlay=true;
			revindFlv(startFromSecond);
			isPlay.v=true;
		}
		
		public function isAtStart():Boolean{
			return netStream.time<1;
		}
		
		public function isAtEnd():Boolean{
			if(metaData){
				return Math.round(netStream.time)==Math.round(metaData.duration);
			}
			return false;
		}
		
		public function getTime():Number {
			if(!netStream || isNaN(netStream.time)){ return NaN; }
			return netStream.time;
		}
		public function getDuration():Number {
			if(!metaData){ return NaN; }
			return metaData.duration;
		}
		
		public function closeStream():void {
			netStream.close();
			ef.removeEventListener(ef.event_EnterFrame, onEnterFrame);
			isPlay.v=false;
			isStartedStream=false;
		}
		
		public function kill():void {
			closeStream();
			video=null;
		}
		
		public function setIsPlaying(playing:Boolean):void {
			if(playing){
				if(isAtEnd()){
					revindFlvAndPlay();
				}else{
					netStream.resume();
				}
			}else{
				netStream.pause();
			}
		}

		public function getGotMetaData():Boolean {
			return Boolean(metaData);
		}

		public function getMetaData():Object {
			return metaData;
		}
		
//****************************************************************************
// EVENTS for FLVPlayer
//****************************************************************************
		public function onEvent(ev:MLEventInfo):void {
			switch(ev.hero){
				case isPlay:
					switch(ev.event) {
						case isPlay.event_Changed:
							if(isPlay.v){
								if(isAtEnd()){
									revindFlvAndPlay();
								}else{
									netStream.resume();
								}
							}else{
								netStream.pause();	
							}
						break;
					}
				break;
				case volume:
					switch(ev.event) {
						case volume.event_Changed:
							if(netStream && netStream.soundTransform){
								var videoVolumeTransform:SoundTransform = new SoundTransform();
								videoVolumeTransform.volume = volume.v;
								netStream.soundTransform = videoVolumeTransform;
							}
							broadcastEvent(event_SharedObjectDataChanged);
						break;
					}
				break;
				case screenGrabber:
					switch(ev.event) {
						case screenGrabber.event_ScreenGrabbed:
							broadcastEvent(event_ScreenGrabbed, ev.data);
							screenGrabber.removeListener(this);
							screenGrabber=null;
						break;
					}
				break;
			}
		}
		
		private function onStreamNetStatus(event:NetStatusEvent):void {
			var infoObject:Object=event.info;
			switch(infoObject.level){
				case 'error':
				break;
				case 'status':
					dbg.log('code:'+infoObject.code);
					switch(infoObject.code){
						case 'NetStream.Buffer.Empty':
							if(isPlaying.v){
								if(isAtEnd()){
									isPlay.v=false;
									broadcastEvent(event_MovieIsFinished);
								}else{
									isBuffering.v=true;
								}
							}else{
								if(isAtEnd()){
									playingProgress.v=1;
								}
							}
						break;
						case 'NetStream.Buffer.Flush':
							if(isAtEnd()){
								isPlay.v=false;
							}
						break;
						case 'NetStream.Seek.Notify':
							dbg.log('NetStream.Seek.Notify isNetStreamPlayStart:'+isNetStreamPlayStart);
							if(isNetStreamPlayStart){
								isNetStreamPlayStart=false;
								isPlay.v=true;
							}else{
								if(isRevindFlvAndPlay){
									isRevindFlvAndPlay=false;
									isPlay.v=true;
									netStream.resume();
								}else if(isAtEnd()) {
									isPlay.v=false;
								}
								isRevindFlvAndPlay=false;
							}
						break;
						case 'NetStream.Buffer.Full':
							isBuffering.v=false;
						break;
						case 'NetStream.Play.Start':
							isNetStreamPlayStart=true;
							isPlay.v=true;
							isPlaying.v=true;
							netStream.resume();
						break;
						case 'NetStream.Play.Stop':
							if(isAtEnd()){
								isPlay.v=false;
								broadcastEvent(event_MovieIsFinished);
							}
							isPlaying.v=false;
						break;
						case 'NetStream.Play.StreamNotFound':
							ef.removeEventListener(ef.event_EnterFrame, onEnterFrame);
							isPlay.v=false;
							isStreamNotFound.v=true;
						break;
					}
				break;
			}
		}
		
		public function onMetaData(info:Object):void {
			if(metaData){ return; }
			metaData=info;
			broadcastEvent(event_MetaData);
		}
		
		public function onCuePoint(info:Object):void {
			
		}
		
		public function onPlayStatus(info:Object):void {
			
		}
		
		public function onEnterFrame(event:MLEvent):void {
			loadingProgress.v=netStream.bytesLoaded/netStream.bytesTotal;
			if(metaData){
				playingProgress.v=netStream.time/metaData.duration;
				if(metaData.duration-netStream.time<0.2){
					//logv(fileUrl, metaData.duration+'-'+netStream.time+'='+(metaData.duration-netStream.time));
					broadcastEvent(event_Movie100MSBeforeFinished);
				}
			}
		}
		
		public function getSharedObjectData():Object {
			return {
				v:volume.v
			};
		}
		
		public function getSharedObjectInitData():Object {
			return {
				v:0.8
			};
		}
		
		public function getSharedObjectUniqueName():String {
			return 'flvPlayer';
		}
		
		public function applyCookieData(data:Object):void {
			volume.v=data.v;
		}
		
		public function get event_SharedObjectDataChanged():String { return 'SharedObjectDataChanged'; }
		
	}
}
